@file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry", "SuspiciousCollectionReassignment")

package com.gitee.wsl.compose.ui.richtext.content

import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.buildAnnotatedString
import com.gitee.wsl.compose.ui.richtext.content.RichTextString.Builder
import com.gitee.wsl.compose.ui.richtext.content.Format.Companion.FormatAnnotationScope
import com.gitee.wsl.compose.ui.richtext.theme.RichTextStringStyle
import com.gitee.wsl.compose.ui.richtext.content.inline.InlineContent
import com.gitee.wsl.struct.generator.randomUUID

/** Copied from inline content. */
@PublishedApi
internal const val REPLACEMENT_CHAR: String = "\uFFFD"

/**
 * Convenience function for creating a [RichTextString] using a [Builder].
 */
 inline fun richTextString(builder: Builder.() -> Unit): RichTextString = Builder().apply(builder).toRichTextString()

/**
 * A special type of [AnnotatedString] that is formatted using higher-level directives that are
 * configured using a [RichTextStringStyle].
 */
@Immutable
data class RichTextString internal constructor(
  private val taggedString: AnnotatedString,
  internal val formatObjects: Map<String, Any>,
  val paragraphContents: List<ParagraphContent>
) {

  private val length: Int get() = taggedString.length
  val text: String get() = taggedString.text

   operator fun plus(other: RichTextString): RichTextString =
    Builder(length + other.length).run {
      append(this@RichTextString)
      append(other)
      toRichTextString()
    }

  fun toAnnotatedString(
       style: RichTextStringStyle,
       contentColor: Color
  ): AnnotatedString =
    buildAnnotatedString {
      append(taggedString)

      // Get all of our format annotations.
      val tags = taggedString.getStringAnnotations(FormatAnnotationScope, 0, taggedString.length)
      // And apply their actual SpanStyles to the string.
      tags.forEach { range ->
        val format = Format.findTag(range.item, formatObjects) ?: return@forEach
        format.getStyle(style, contentColor)?.let { spanStyle -> addStyle(spanStyle, range.start, range.end) }
      }
    }

  internal fun getInlineContents(): Map<String, InlineContent> =
    formatObjects.asSequence()
      .mapNotNull { (tag, format) ->
        tag.removePrefix("inline:")
          // If no prefix was found then we ignore it.
          .takeUnless { it === tag }
          ?.let {
            @Suppress("UNCHECKED_CAST")
            Pair(it, format as InlineContent)
          }
      }
      .toMap()

    class Builder(capacity: Int = 16) {
    private val builder = AnnotatedString.Builder(capacity)
    private val formatObjects = mutableMapOf<String, Any>()
    private val paragraphs = mutableListOf<ParagraphContent>()
     fun addFormat(
         format: Format,
         start: Int,
         end: Int
    ) {
      val tag = format.registerTag(formatObjects)
      builder.addStringAnnotation(FormatAnnotationScope, tag, start, end)
    }

     fun pushFormat(format: Format): Int {
      val tag = format.registerTag(formatObjects)
      return builder.pushStringAnnotation(FormatAnnotationScope, tag)
    }

     fun pop(): Unit = builder.pop()

     fun pop(index: Int): Unit = builder.pop(index)

     fun append(text: String): Unit = builder.append(text)

     fun append(text: RichTextString) {
      builder.append(text.taggedString)
      formatObjects.putAll(text.formatObjects)
    }

     fun appendInlineContent(
         alternateText: String = REPLACEMENT_CHAR,
         content: InlineContent
    ) {
      val tag = randomUUID()
      formatObjects["inline:$tag"] = content
      builder.appendInlineContent(tag, alternateText)
    }

    /**
     * Provides access to the underlying builder, which can be used to add arbitrary formatting,
     * including mixed with formatting from this Builder.
     */
     fun <T> withAnnotatedString(block: AnnotatedString.Builder.() -> T): T = builder.block()

     fun toRichTextString(): RichTextString =
      RichTextString(
        builder.toAnnotatedString(),
        formatObjects.toMap(),
        paragraphs)
  }
}

 inline fun Builder.withFormat(
     format: Format,
     block: Builder.() -> Unit
) {
  val index = pushFormat(format)
  block()
  pop(index)
}

